package com.wayz.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Strings;

/**
 * 加密辅助类
 * 
 * @author:mike.ma
 *
 */
public final class EncryptHelper {

	/** 日志相关 */
	private static final Logger LOGGER = LoggerFactory.getLogger(EncryptHelper.class);

	/** 安全的随机数源 */
	private static final SecureRandom RANDOM = new SecureRandom();
	/** SHA-1加密 */
	private static MessageDigest SHA_1 = null;
	/** DES加密算法 */
	private static final String DES_ALGORITHM = "DESede"; // 可用
	/** AES加密算法 */
	private static final String AES_ALGORITHM = "AES";
	/** DES默认加密 */
	private static Cipher DES_CIPHER_ENC = null;
	/** DES默认解密 */
	private static Cipher DES_CIPHER_DEC = null;
	/** DES默认key */
	private static final String DEFAULT_DES_KEY = "EqYKiaWy6qBRvX0EUoUXC6O3xEeRhz";

	private EncryptHelper() {

	}

	static {
		initSHA1();
		initDES();
	}

	/** 初始化sh1 */
	private static void initSHA1() {
		try {
			SHA_1 = MessageDigest.getInstance("SHA-1");
		}
		catch (NoSuchAlgorithmException e) {
			throw new IllegalStateException(e);
		}
	}

	/** 初始化DES */
	@SuppressWarnings("restriction")
	private static void initDES() {

		// 添加JCE算法
		Security.addProvider(new com.sun.crypto.provider.SunJCE());
		// 初始化默认DES加密
		try {
			// 密钥
			SecretKey desKey = new SecretKeySpec(new byte[] { 0x11, 0x22, 0x4F, 0x58, (byte) 0x88, 0x10, 0x40, 0x38,
					0x28, 0x25, 0x79, 0x51, (byte) 0xCB, (byte) 0xDD, 0x55, 0x66, 0x77, 0x29, 0x74, (byte) 0x98, 0x30,
					0x40, 0x36, (byte) 0xE2 }, DES_ALGORITHM);
			// 初始化默认加密
			DES_CIPHER_ENC = Cipher.getInstance(DES_ALGORITHM);
			DES_CIPHER_ENC.init(Cipher.ENCRYPT_MODE, desKey, RANDOM);
			// 初始化默认解密
			DES_CIPHER_DEC = Cipher.getInstance(DES_ALGORITHM);
			DES_CIPHER_DEC.init(Cipher.DECRYPT_MODE, desKey, RANDOM);
		}
		catch (Exception e) {
			LOGGER.error("DES默认加密解密初始化失败[error:{}]", e.getMessage());
			throw new RuntimeException("DES默认加密解密初始化失败:" + e.getMessage(), e);
		}
	}

	/**
	 * 
	 * <p>SHA-1加密</p>
	 * 
	 * @param str 明文
	 * @return 密文
	 * @date 2017年9月19日
	 */
	public static String sha1(String str) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		return Base64.encodeBase64String(SHA_1.digest(str.getBytes()));
	}

	/**
	 * 
	 * <p>SHA-1加密(Url安全)</p>
	 * 
	 * @param str 明文
	 * @return 密文
	 * @date 2017年9月19日
	 */
	public static String sha1Url(String str) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		return Base64.encodeBase64URLSafeString(SHA_1.digest(str.getBytes()));
	}

	/**
	 * 
	 * <p>Base64编码</p>
	 * 
	 * @param binaryData byte数组
	 * @return 编码后的byte数组
	 * @date 2017年9月19日
	 */
	public static byte[] encode64(byte[] binaryData) {
		return Base64.encodeBase64(binaryData);
	}

	/**
	 * 
	 * <p>Base64编码字符串</p>
	 * 
	 * @param str 需要编码的字符串
	 * @return 编码后的字符串
	 * @date 2017年9月19日
	 */
	public static String encode64(String str) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		return Base64.encodeBase64String(str.getBytes());
	}

	/**
	 * 
	 * <p>Base64编码字符串(Url安全)</p>
	 * 
	 * @param str 需要编码的字符串
	 * @return 编码后的字符串
	 * @date 2017年9月19日
	 */
	public static String url64(String str) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		return Base64.encodeBase64URLSafeString(str.getBytes());
	}

	/**
	 * 
	 * <p> Base64解码</p>
	 * 
	 * @param binaryData byte数组
	 * @return 解码后的byte数组
	 * @date 2017年9月19日
	 */
	public static byte[] decode64(byte[] binaryData) {
		return Base64.encodeBase64(binaryData);
	}

	/**
	 * 
	 * <p>Base64解码字符串</p>
	 * 
	 * @param str 需要解码的字符串
	 * @return 解码后的字符串
	 * @date 2017年9月19日
	 */
	public static String decode64(String str) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		return Base64.encodeBase64String(str.getBytes());

	}

	/**
	 * <p>生成32位MD5密文</p>
	 * 
	 * <pre> org.apache.commons.codec.digest.DigestUtils </pre>
	 * 
	 * @param str 明文
	 * @return 密文
	 */
	public static String to32MD5(String str) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		return DigestUtils.md5Hex(str);
	}

	/**
	 * 
	 * <p>DES加密(默认密钥)</p>
	 * 
	 * @param str 需要加密的明文
	 * @return 加密后的密文(base64编码字符串)
	 * @date 2017年9月19日
	 */
	public static String desEncryp(String str) {
		return desEncryp(str, false);
	}

	/**
	 * 
	 * <p> DES加密(默认密钥)</p>
	 * 
	 * @param str 需要加密的明文
	 * @return 加密后的密文(base64编码字符串,Url安全)
	 * @date 2017年9月19日
	 */
	public static String desEncrypUrl(String str) {
		return desEncryp(str, true);
	}

	/**
	 * 
	 * <p>DES加密(默认密钥)</p>
	 * 
	 * @param str 需要加密的明文
	 * @param urlSafety 密文是否需要Url安全
	 * @return 加密后的密文(str为null返回null)
	 * @date 2017年9月19日
	 */
	public static String desEncryp(String str, boolean urlSafety) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}

		try {
			byte[] bytes = DES_CIPHER_ENC.doFinal(str.getBytes(Charsets.UTF_8));// 加密
			if (urlSafety) {
				return Base64.encodeBase64URLSafeString(bytes);
			}
			return Base64.encodeBase64String(bytes);
		}
		catch (Exception e) {
			LOGGER.error("DES加密失败[密文:{},error:{}]", str, e.getMessage());
			return null;
		}
	}

	/**
	 * 
	 * <p>DES解密(默认密钥)</p>
	 * 
	 * @param str 需要解密的密文(base64编码字符串)
	 * @return 解密后的明文
	 * @date 2017年9月19日
	 */
	public static String desDecrypt(String str) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}

		try {
			byte[] bytes = DES_CIPHER_DEC.doFinal(Base64.decodeBase64(str));// 解密
			return new String(bytes, Charsets.UTF_8);
		}
		catch (Exception e) {
			LOGGER.error("DES解密失败[密文:{},error:{}]", str, e.getMessage());
			return null;
		}

	}

	/**
	 * 
	 * <p>DES加密</p>
	 * 
	 * @param str 需要加密的明文
	 * @param key key 密钥(长度小于24字节自动补足，大于24取前24字节)
	 * @return 加密后的密文(base64编码字符串)
	 * @date 2017年9月19日
	 */
	public static String desEncryp(String str, String key) {
		return desEncryp(str, key, false);
	}

	/**
	 * 
	 * <p>DES加密</p>
	 * 
	 * @param str 需要加密的明文
	 * @param key 密钥(长度小于24字节自动补足，大于24取前24字节)
	 * @return 加密后的密文(base64编码字符串,Url安全)
	 * @date 2017年9月19日
	 */
	public static String desEncrypUrl(String str, String key) {
		return desEncryp(str, key, true);
	}

	/**
	 * DES加密
	 * 
	 * @param str 需要加密的明文
	 * @param key 密钥(长度小于24字节自动补足，大于24取前24字节)
	 * @param urlSafety 密文是否需要Url安全
	 * @return 加密后的密文
	 */
	public static String desEncryp(String str, String key, boolean urlSafety) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		if (Strings.isNullOrEmpty(key)) {
			key = DEFAULT_DES_KEY;
		}

		try {
			Cipher c = Cipher.getInstance(DES_ALGORITHM);
			c.init(Cipher.ENCRYPT_MODE, desKey(key), RANDOM);
			// 加密
			byte[] bytes = c.doFinal(str.getBytes(Charsets.UTF_8));// 加密
			// 返回b64处理后的字符串
			if (urlSafety) {
				return Base64.encodeBase64URLSafeString(bytes);
			}
			return Base64.encodeBase64String(bytes);

		}
		catch (Exception e) {
			LOGGER.error("DES加密失败[密文:{},key:{},error:{}]", str, key, e.getMessage());
			return null;
		}
	}

	/**
	 * 
	 * <p> DES解密</p>
	 * 
	 * @param str 需要解密的密文(base64编码字符串)
	 * @param key 密钥(长度小于24字节自动补足，大于24取前24字节)
	 * @return 解密后的明文
	 * @date 2017年9月19日
	 */
	public static String desDecrypt(String str, String key) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		if (Strings.isNullOrEmpty(key)) {
			key = DEFAULT_DES_KEY;
		}

		try {
			Cipher c = Cipher.getInstance(DES_ALGORITHM);
			c.init(Cipher.DECRYPT_MODE, desKey(key), RANDOM);
			byte[] bytes = c.doFinal(Base64.decodeBase64(str));

			return new String(bytes, Charsets.UTF_8);
		}
		catch (Exception e) {
			LOGGER.error("DES解密失败[密文:{},key:{},error:{}]", str, key, e.getMessage());
		}

		return null;
	}

	/**
	 * 
	 * <p> DES密钥</p>
	 * 
	 * @param key
	 * @return
	 * @date 2017年9月19日
	 */
	private static SecretKey desKey(String key) {
		byte[] bs = key.getBytes();
		if (bs.length != 24) {
			bs = Arrays.copyOf(bs, 24);// 处理数组长度为24
		}
		return new SecretKeySpec(bs, DES_ALGORITHM);
	}

	/**
	 * 
	 * <p>AES加密</p>
	 * 
	 * @param str 需要加密的明文
	 * @param key 密钥
	 * @return 加密后的密文
	 * @date 2017年9月19日
	 */
	public static String aesEncryp(String str, String key) {
		return aesEncryp(str, key, false);
	}

	/**
	 * 
	 * <p>AES加密</p>
	 * 
	 * @param str 需要加密的明文
	 * @param key 密钥
	 * @param urlSafety 密文是否需要Url安全
	 * @return 加密后的密文
	 * @date 2017年9月19日
	 */
	public static String aesEncryp(String str, String key, boolean urlSafety) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		if (Strings.isNullOrEmpty(key)) {
			key = DEFAULT_DES_KEY;
		}

		try {
			Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
			c.init(Cipher.ENCRYPT_MODE, aesKey(key), RANDOM);
			byte[] bytes = c.doFinal(str.getBytes(Charsets.UTF_8));// 加密
			if (urlSafety) {
				return Base64.encodeBase64URLSafeString(bytes);
			}
			return Base64.encodeBase64String(SHA_1.digest(str.getBytes()));
		}
		catch (Exception e) {
			LOGGER.error("AES解密失败[密文:{},key:{},error:{}]", str, key, e.getMessage());
		}
		return null;
	}

	/**
	 * 
	 * <p>AES解密</p>
	 * 
	 * @param str 需要解密的密文(base64编码字符串)
	 * @param key 密钥
	 * @return 解密后的明文
	 * @date 2017年9月19日
	 */
	public static String aesDecrypt(String str, String key) {
		if (Strings.isNullOrEmpty(str)) {
			return null;
		}
		if (Strings.isNullOrEmpty(key)) {
			key = DEFAULT_DES_KEY;
		}
		try {
			Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
			c.init(Cipher.DECRYPT_MODE, aesKey(key), RANDOM);
			return new String(c.doFinal(Base64.decodeBase64(str)), Charsets.UTF_8);// 解密
		}

		catch (Exception e) {
			LOGGER.error("AES加密失败[密文:{},key:{},error:{}]", str, key, e.getMessage());

		}
		return null;
	}

	/**
	 * 
	 * <p>AES密钥 </p>
	 * 
	 * @param key
	 * @return
	 * @date 2017年9月19日
	 */
	private static SecretKeySpec aesKey(String key) {
		byte[] bs = key.getBytes();
		if (bs.length != 16) {
			bs = Arrays.copyOf(bs, 16);// 处理数组长度为16
		}
		return new SecretKeySpec(bs, AES_ALGORITHM);
	}

	/**
	 * 转化MD5编码
	 * 
	 * @param value 字符串值
	 * @return MD5编码
	 */
	public static String toMD5(String value) {
		try {
			MessageDigest digest = MessageDigest.getInstance("MD5");
			digest.update(value.getBytes());
			return HexHelper.toHexFromBytes(digest.digest());
		}
		catch (NoSuchAlgorithmException e) {
			LOGGER.warn("转化MD5编码异常", e);
		}
		return "";
	}

	/**
	 * 加密
	 * 
	 * @param value
	 * @return 加密字符串
	 */
	public static String encode(String value) {
		byte[] by = value.getBytes();
		for (int i = 0; i < by.length; i++) {
			byte s = by[i];
			s = (byte) (s ^ 18);
		}
		return HexHelper.toHexFromBytes(by);
	}

	/**
	 * 解密
	 * 
	 * @param value
	 * @return 解密字符串
	 */
	public static String decode(String value) {
		byte[] by = HexHelper.toBytesFromHex(value);
		for (int i = 0; i < by.length; i++) {
			byte s = by[i];
			s = (byte) (s ^ 18);
		}

		return new String(by);
	}

	public static void main(String args[]) {
		// md
		System.out.println(toMD5("http://101.201.30.129:8980/zentao/bug-browse-5.html").toUpperCase());
	}
}
